package com.lincoln.cloud.servercenter.abstracter;

import com.alibaba.fastjson.JSONObject;
import com.lincoln.cloud.common.bean.CommonServerInfo;
import com.lincoln.cloud.common.bean.IpAndPort;
import com.lincoln.cloud.common.bean.ServerMessage;
import com.lincoln.cloud.common.utils.ByteUtil;
import com.lincoln.cloud.common.utils.CommondValue;
import com.lincoln.cloud.servercenter.bean.ServerCenterContext;
import com.lincoln.cloud.servercenter.bean.ServerInfo;
import com.lincoln.cloud.servercenter.interfaces.BaseServerInfo;
import com.lincoln.cloud.servercenter.interfaces.CommunicationProtocol;
import com.lincoln.framework.NettyServer;
import com.lincoln.framework.bean.AbstractMsgNoDecoder;
import com.lincoln.framework.bean.AbstractMsgNoEncoder;
import com.lincoln.framework.bean.AbstractMsgNoProtocol;
import com.lincoln.framework.bean.NettyContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

/**
 * CommunicationProtocol的netty实现
 *
 * @author lincoln
 */
public class NettyCommunicationProtocol implements CommunicationProtocol {
    private static final Logger logger = LoggerFactory.getLogger(NettyCommunicationProtocol.class);

    @Override
    public boolean init() throws Exception {
        //启动netty服务器
        NettyServer.setNettyServer(new NettyServer(9999, 300, AbstractMsgNoDecoder.class, AbstractMsgNoEncoder.class, AbstractMsgNoProtocol.class, ServerInfo.class));
        NettyServer.scanProtocol("com.lincoln.cloud.servercenter.protocol");
        NettyServer.runServer();
        return true;
    }

    @Override
    public boolean sendMessage(ServerMessage serverMessage) {
        if (ByteUtil.compare(serverMessage.getCmd(), CommondValue.ACHIEVE_SERVER_CMD.getBytes())) {
            logger.info("本次长度:" + serverMessage2protocol(serverMessage).size());
        }
        serverMessage.setFrom(-1);
        for (int to : serverMessage.getTos()) {
            BaseServerInfo baseServerInfo = ServerCenterContext.serverPool().findById(to);
            if (baseServerInfo == null || !(baseServerInfo instanceof ServerInfo)) {
                return false;
            }
        }
        for (int to : serverMessage.getTos()) {
            BaseServerInfo baseServerInfo = ServerCenterContext.serverPool().findById(to);
            ServerInfo serverInfo = (ServerInfo) baseServerInfo;
            serverInfo.getChannelHandlerContext().channel().writeAndFlush(serverMessage2protocol(serverMessage));
        }
        return true;
    }

    @Override
    public boolean login(int deviceType, int protocolType, String name, int httpPort) {
        BaseServerInfo deviceInfo = NettyContext.getDevice();
        if (null == name || "".equals(name)) {
            return false;
        }
        //生成id
        deviceInfo.setPrimaryKey(ServerCenterContext.serverPool().createId());
        deviceInfo.setName(name);
        deviceInfo.setHttpPort(httpPort);
        deviceInfo.setDeviceType(deviceType);
        deviceInfo.setProtocolType(protocolType);
        deviceInfo.setFirstContact(new Date());
        boolean res = deviceInfo.login();
        if (res) {
            logger.info(deviceInfo.getPrimaryKey() + "登陆成功");
            ServerMessage serverMessage = new ServerMessage();
            serverMessage.setCmd(CommondValue.LOGIN_SERVER_CMD.getBytes());
            serverMessage.setTos(new int[]{deviceInfo.getPrimaryKey()});
            serverMessage.setMsg(ByteUtil.append(new byte[]{(byte) CommondValue.SUCCESS}, ByteUtil.int2byte(deviceInfo.getPrimaryKey())));
            return sendMessage(serverMessage);
        } else {
            return false;
        }
    }

    @Override
    public boolean checkHeart() {
        BaseServerInfo deviceInfo = NettyContext.getDevice();
        deviceInfo.setLastContact(new Date());
        ServerMessage serverMessage = new ServerMessage();
        serverMessage.setCmd(CommondValue.HEART_BEAT_SERVER_CMD.getBytes());
        serverMessage.setTos(new int[]{deviceInfo.getPrimaryKey()});
        serverMessage.setMsg(new byte[]{(byte) CommondValue.SUCCESS});
        return sendMessage(serverMessage);
    }

    /**
     * 错误信息:未发现服务
     */
    private static final int ERROR_NO_SERVER_FOUND = 0;

    @Override
    public boolean achieveServer(String name) {
        BaseServerInfo deviceInfo = NettyContext.getDevice();
        List<BaseServerInfo> list = ServerCenterContext.serverPool().findByName(name);
        if (list == null || list.size() <= 0) {
            ServerMessage serverMessage = new ServerMessage();
            serverMessage.setCmd(CommondValue.ACHIEVE_SERVER_CMD.getBytes());
            serverMessage.setTos(new int[]{deviceInfo.getPrimaryKey()});
            serverMessage.setMsg(new byte[]{(byte) CommondValue.FAIL, (byte) ERROR_NO_SERVER_FOUND});
            return sendMessage(serverMessage);
        } else {
            int random = new Random().nextInt(list.size());
            ServerMessage serverMessage = new ServerMessage();
            serverMessage.setCmd(CommondValue.ACHIEVE_SERVER_CMD.getBytes());
            serverMessage.setTos(new int[]{deviceInfo.getPrimaryKey()});
            serverMessage.setMsg(ByteUtil.append(new byte[]{(byte) CommondValue.SUCCESS}, baseServerInfo2commonServerInfo(list.get(random)).toString().getBytes()));
            return sendMessage(serverMessage);
        }
    }

    @Override
    public boolean achieveServers(String name) throws Exception {
        BaseServerInfo deviceInfo = NettyContext.getDevice();
        List<BaseServerInfo> list = ServerCenterContext.serverPool().findByName(name);
        if (list == null || list.size() <= 0) {
            ServerMessage serverMessage = new ServerMessage();
            serverMessage.setCmd(CommondValue.MUL_ACHIEVE_SERVER_CMD.getBytes());
            serverMessage.setTos(new int[]{deviceInfo.getPrimaryKey()});
            serverMessage.setMsg(new byte[]{(byte) CommondValue.FAIL, (byte) ERROR_NO_SERVER_FOUND});
            return sendMessage(serverMessage);
        } else {
            ServerMessage serverMessage = new ServerMessage();
            serverMessage.setCmd(CommondValue.MUL_ACHIEVE_SERVER_CMD.getBytes());
            serverMessage.setTos(new int[]{deviceInfo.getPrimaryKey()});
            serverMessage.setMsg(ByteUtil.append(new byte[]{(byte) CommondValue.SUCCESS}, JSONObject.toJSONString(baseServerInfo2commonServerInfo(list)).getBytes()));
            return sendMessage(serverMessage);
        }
    }

    @Override
    public boolean achieveServer(int id) throws Exception {
        BaseServerInfo deviceInfo = NettyContext.getDevice();
        BaseServerInfo baseServerInfo = ServerCenterContext.serverPool().findById(id);
        if (baseServerInfo == null) {
            ServerMessage serverMessage = new ServerMessage();
            serverMessage.setCmd(CommondValue.ACHIEVE_SERVER_BY_ID_CMD.getBytes());
            serverMessage.setTos(new int[]{deviceInfo.getPrimaryKey()});
            serverMessage.setMsg(new byte[]{(byte) CommondValue.FAIL, (byte) ERROR_NO_SERVER_FOUND});
            return sendMessage(serverMessage);
        } else {
            ServerMessage serverMessage = new ServerMessage();
            serverMessage.setCmd(CommondValue.ACHIEVE_SERVER_BY_ID_CMD.getBytes());
            serverMessage.setTos(new int[]{deviceInfo.getPrimaryKey()});
            serverMessage.setMsg(ByteUtil.append(new byte[]{(byte) CommondValue.SUCCESS}, baseServerInfo2commonServerInfo(baseServerInfo).toString().getBytes()));
            return sendMessage(serverMessage);
        }
    }

    @Override
    public boolean achieveServers() throws Exception {
        BaseServerInfo deviceInfo = NettyContext.getDevice();
        List<BaseServerInfo> list = ServerCenterContext.serverPool().findAll();
        if (list == null || list.size() <= 0) {
            ServerMessage serverMessage = new ServerMessage();
            serverMessage.setCmd(CommondValue.ALL_ACHIEVE_SERVER_CMD.getBytes());
            serverMessage.setTos(new int[]{deviceInfo.getPrimaryKey()});
            serverMessage.setMsg(new byte[]{(byte) CommondValue.FAIL, (byte) ERROR_NO_SERVER_FOUND});
            return sendMessage(serverMessage);
        } else {
            ServerMessage serverMessage = new ServerMessage();
            serverMessage.setCmd(CommondValue.ALL_ACHIEVE_SERVER_CMD.getBytes());
            serverMessage.setTos(new int[]{deviceInfo.getPrimaryKey()});
            serverMessage.setMsg(ByteUtil.append(new byte[]{(byte) CommondValue.SUCCESS}, JSONObject.toJSONString(baseServerInfo2commonServerInfo(list)).getBytes()));
            return sendMessage(serverMessage);
        }
    }

    @Override
    public boolean updateInfo(CommonServerInfo commonServerInfo) throws Exception {
        BaseServerInfo deviceInfo = NettyContext.getDevice();
        if (commonServerInfo.getId() == deviceInfo.getPrimaryKey()) {
            deviceInfo.setAuthor(commonServerInfo.getAuthor());
            deviceInfo.setDeviceType(commonServerInfo.getDeviceType());
            deviceInfo.setProtocolType(commonServerInfo.getProtocolType());
            deviceInfo.setIntroduce(commonServerInfo.getIntroduce());
            deviceInfo.setHttpPort(commonServerInfo.getIpAndPort().getPort());
            ServerMessage serverMessage = new ServerMessage();
            serverMessage.setCmd(CommondValue.UPDATE_INFO_SERVER_CMD.getBytes());
            serverMessage.setTos(new int[]{deviceInfo.getPrimaryKey()});
            serverMessage.setMsg(new byte[]{(byte) CommondValue.SUCCESS});
            return sendMessage(serverMessage);
        } else {
            ServerMessage serverMessage = new ServerMessage();
            serverMessage.setCmd(CommondValue.UPDATE_INFO_SERVER_CMD.getBytes());
            serverMessage.setTos(new int[]{deviceInfo.getPrimaryKey()});
            serverMessage.setMsg(new byte[]{(byte) CommondValue.FAIL});
            return sendMessage(serverMessage);
        }

    }

    /**
     * AbstractMsgNoProtocol到ServerMessage的转换
     *
     * @param protocol
     * @return
     */
    public static ServerMessage protocol2serverMessage(AbstractMsgNoProtocol protocol) {
        byte[] payload = protocol.getPayload();
        int from = ByteUtil.byte2int(ByteUtil.sub(payload, 0, 4));
        payload = ByteUtil.sub(payload, 4);
        int toLength = ByteUtil.byte2int(ByteUtil.sub(payload, 0, 4));
        payload = ByteUtil.sub(payload, 4);
        int[] tos = new int[toLength];
        for (int i = 0; i < toLength; i++) {
            tos[i] = ByteUtil.byte2int(ByteUtil.sub(payload, 0, 4));
            payload = ByteUtil.sub(payload, 4);
        }
        ServerMessage serverMessage = new ServerMessage();
        serverMessage.setFrom(from);
        serverMessage.setTos(tos);
        serverMessage.setNo(protocol.getNo());
        serverMessage.setCmd(protocol.getCmd());
        serverMessage.setMsg(payload);
        return serverMessage;
    }

    /**
     * ServerMessage到AbstractMsgNoProtocol的转换
     *
     * @param serverMessage
     * @return 返回的是接收人的那部分payload
     */
    public static AbstractMsgNoProtocol serverMessage2protocol(ServerMessage serverMessage) {
        byte[] res = ByteUtil.append(ByteUtil.int2byte(serverMessage.getFrom()), ByteUtil.int2byte(serverMessage.getTos().length));
        for (int to : serverMessage.getTos()) {
            res = ByteUtil.append(res, ByteUtil.int2byte(to));
        }
        res = ByteUtil.append(res, serverMessage.getMsg());
        AbstractMsgNoProtocol protocol = new AbstractMsgNoProtocol(serverMessage.getNo(), serverMessage.getCmd(), res);
        return protocol;
    }

    /**
     * 转换传输格式
     *
     * @param baseServerInfo
     * @return
     */
    private CommonServerInfo baseServerInfo2commonServerInfo(BaseServerInfo baseServerInfo) {
        CommonServerInfo commonServerInfo = new CommonServerInfo();
        commonServerInfo.setId(baseServerInfo.getPrimaryKey());
        commonServerInfo.setAuthor(baseServerInfo.getAuthor());
        commonServerInfo.setDeviceType(baseServerInfo.getDeviceType());
        commonServerInfo.setProtocolType(baseServerInfo.getProtocolType());
        commonServerInfo.setFirstContact(baseServerInfo.getFirstContact());
        commonServerInfo.setIntroduce(baseServerInfo.getIntroduce());
        commonServerInfo.setIpAndPort(new IpAndPort(baseServerInfo.getIp(), baseServerInfo.getPort()));
        commonServerInfo.setLastContact(baseServerInfo.getLastContact());
        commonServerInfo.setServerName(baseServerInfo.getName());
        return commonServerInfo;
    }

    /**
     * 转换传输格式
     *
     * @param baseServerInfos
     * @return
     */
    private List<CommonServerInfo> baseServerInfo2commonServerInfo(List<BaseServerInfo> baseServerInfos) {
        if (baseServerInfos == null) {
            return new ArrayList<>();
        }
        List<CommonServerInfo> res = new ArrayList<>();
        for (BaseServerInfo baseServerInfo : baseServerInfos) {
            res.add(baseServerInfo2commonServerInfo(baseServerInfo));
        }
        return res;
    }

}
